import pygame, time, os.path
from map import *
from tank import *
from aimer import *
from game_server import *
from event_handler import *
from loop_client import *
from hud import *
from health_bar import *
from ammo_bar import *
from game_condition import *
from score_bar import *

class game_logic:
    def __init__(self, game):
        self.game = game
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.bind(("", 1976))
        self.sock.settimeout(5)
    
    def setup_host(self):
        #pygame.init()
        #pygame.event.get()
        self.local_player = self.game.config.local_player
        self.setup_server = game_setup_server(self.game)
        self.screen = self.game.screen
        self.screen_rect = self.game.screen_rect
        self.map = map(os.path.join("data", "maps", self.game.config.options['map']), self.game)
        self.event_handler = self.game.event_handler
        self.end_condition = deathmatch(self.game)
        self.user_events = []
        self.colliders = self.map.colliders
        
        self.tank_list = []
        self.turret_list = []
        self.obj_list = []
        self.tankFactory = tank_factory(self.game, self.tank_list, self.turret_list)
        
        for i in self.game.players:
            self.game.players[i].start_info['start_position'] = self.map.config['start_positions'][i]
            self.game.players[i].tank = self.tankFactory.get_tank(i, self.game.players[i])
            self.game.players[i].tank.set_pos(self.map.config['start_positions'][i])
            self.game.players[i].reset_round()
        
        self.local_player.tank.is_local = True
        
        if len(self.game.players) > 1:
            print "Awaiting Readies"
            if self.setup_server.start_game():
                print "Readies ready. YEAH!"
            else:
                print "Readies not ready. Bah"
            self.setup_server.close()
            
        self.clock = pygame.time.Clock()
        self.loop_server = game_loop_server(self.game)
        self.loop_quit = False
        self.map_w = self.map.gridSize['width'] * 32
        self.map_h = self.map.gridSize['height'] * 32
        pygame.key.set_repeat(17, 32)
        pygame.mouse.set_visible(False)
        self.aimer = aimer(self)
        self.num_players = len(self.game.players)
        self.hud = hud(self.game)
        self.ammo_bar = ammo_bar(self.game, self.local_player.tank.ammo, 1)
        self.health_bar = health_bar(self.game)
        self.score_bar = score_bar(self.game)
        self.hud.add_obj(self.health_bar)
        self.hud.add_obj(self.ammo_bar)
        self.hud.add_obj(self.score_bar)
        
        print "about to start loop server"
        self.loop_server.start()
        print "loop server running"
        
        
        self.screen.blit(self.map.screen_bg_surf, self.map.screen_bg_surf.get_rect())
        pygame.display.flip()
        self.loop_quit = False
    
    def run_host(self):
        self.clock.tick(60)
        
        self.handle_events()
        self.map.draw_map_objs()
        self.map.draw_objs(self.tank_list)
        
        #find where the top left corner of the screen lies on the map
        self.screen_left = self.local_player.tank.posX - self.screen_rect.w / 2
        if self.screen_left < 0: self.screen_left = 0
        if self.screen_left > self.map_w - self.screen_rect.w: self.screen_left = self.map_w - self.screen_rect.w
        self.screen_top = self.local_player.tank.posY - self.screen_rect.h / 2
        if self.screen_top < 0: self.screen_top = 0
        if self.screen_top > self.map_h - self.screen_rect.h: self.screen_top = self.map_h - self.screen_rect.h
        
        self.map.draw_objs(self.turret_list)
        self.map.draw_objs(self.obj_list)
        if pygame.mouse.get_pos():
            self.map.draw_objs([self.aimer])
        self.screen.blit(self.map.map_surf, self.map.map_rect, pygame.Rect(self.screen_left, self.screen_top, self.screen_rect.w, self.screen_rect.h))
        self.hud.draw()
        pygame.display.flip()
        self.map.clear_objs()
        self.clear_dead_objs(self.obj_list)
        self.user_events = []
        
        if self.end_condition.test():
            self.send_user_event("round_end", self.local_player)
    
    def setup_client(self):
        pygame.init()
        pygame.event.get()
        self.local_player = self.game.config.local_player
        self.screen = self.game.screen
        self.screen_rect = self.game.screen_rect
        self.map = map(os.path.join("data", "maps", self.game.config.options['map']), self.game)
        self.event_handler = self.game.event_handler
        self.user_events = []
        self.colliders = self.map.colliders
        
        self.tank_list = []
        self.turret_list = []
        self.obj_list = []
        self.tankFactory = tank_factory(self.game, self.tank_list, self.turret_list)
        
        for i in range(len(self.game.players)):
            self.game.players[i].tank = self.tankFactory.get_tank(i, self.game.players[i])
            self.game.players[i].tank.set_pos(self.map.config['start_positions'][i])
            self.game.players[i].reset_round()
        
        self.local_player.tank.is_local = True
        
        sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind(("", 1977))
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        sock.settimeout(5)
        
        message = "<tank_game_ready player_id=\"%s\" />" % self.local_player.player_id
        message_len = len(message)
        resend = True
        
        while resend:
            sent = sock.sendto(message, (self.game.config.game_host[0], 1977))
            if not sent < message_len:
                resend = False
        print message
        print self.game.config.game_host
        self.clock = pygame.time.Clock()
        self.loop_client = loop_client(self.game)
        self.loop_quit = False
        self.map_w = self.map.gridSize['width'] * 32
        self.map_h = self.map.gridSize['height'] * 32
        pygame.key.set_repeat(17, 32)
        pygame.mouse.set_visible(False)
        self.aimer = aimer(self)
        self.num_players = len(self.game.players)
        self.hud = hud(self.game)
        self.ammo_bar = ammo_bar(self.game, self.local_player.tank.ammo, 1)
        self.health_bar = health_bar(self.game)
        self.score_bar = score_bar(self.game)
        self.hud.add_obj(self.health_bar)
        self.hud.add_obj(self.ammo_bar)
        self.hud.add_obj(self.score_bar)
        
        self.loop_client.start()
        
        self.screen.blit(self.map.screen_bg_surf, self.map.screen_bg_surf.get_rect())
        pygame.display.flip()
        self.loop_quit = False
    
    def run_client(self):
        self.game.event_handler.run()
        self.clock.tick(60)
        
        self.handle_events()
        
        self.map.draw_map_objs()
        self.map.draw_objs(self.tank_list)
        self.map.draw_objs(self.obj_list)
        
        #find where the top left corner of the screen lies on the map
        self.screen_left = self.local_player.tank.posX - self.screen_rect.w / 2
        if self.screen_left < 0: self.screen_left = 0
        if self.screen_left > self.map_w - self.screen_rect.w: self.screen_left = self.map_w - self.screen_rect.w
        self.screen_top = self.local_player.tank.posY - self.screen_rect.h / 2
        if self.screen_top < 0: self.screen_top = 0
        if self.screen_top > self.map_h - self.screen_rect.h: self.screen_top = self.map_h - self.screen_rect.h
        
        self.map.draw_objs(self.turret_list)
        if pygame.mouse.get_pos():
            self.map.draw_objs([self.aimer])
        self.screen.blit(self.map.map_surf, self.map.map_rect, pygame.Rect(self.screen_left, self.screen_top, self.screen_rect.w, self.screen_rect.h))
        self.hud.draw()
        pygame.display.flip()
        self.map.clear_objs()
        self.clear_dead_objs(self.obj_list)
        self.user_events = []
    
    def clear_dead_objs(self, obj_list):
        for i in range(len(obj_list)-1, -1, -1):
            if not obj_list[i].alive:
                obj = obj_list.pop(i)
                del obj
    
    def handle_events(self):
        for ev in self.game.events:
            if ev.type == pygame.QUIT:
                self.game.stop_loop()
                self.loop_quit = True
            elif ev.type == pygame.KEYDOWN and ev.key == pygame.K_a:
                self.send_user_event("rot_left", self.local_player)
                self.local_player.tank.rotate_left()
            elif ev.type == pygame.KEYDOWN and ev.key == pygame.K_d:
                self.send_user_event("rot_right", self.local_player)
                self.local_player.tank.rotate_right()
            elif ev.type == pygame.KEYDOWN and ev.key == pygame.K_w and self.local_player.tank.speed == 0:
                self.send_user_event("forward", self.local_player)
                self.local_player.tank.forward()
            elif ev.type == pygame.KEYUP and ev.key == pygame.K_w:
                self.send_user_event("stop", self.local_player)
                self.local_player.tank.stop()
            elif ev.type == pygame.KEYDOWN and ev.key == pygame.K_s and self.local_player.tank.speed == 0:
                self.send_user_event("reverse", self.local_player)
                self.local_player.tank.reverse()
            elif ev.type == pygame.KEYUP and ev.key == pygame.K_s:
                self.send_user_event("stop", self.local_player)
                self.local_player.tank.stop()
            elif ev.type == pygame.MOUSEBUTTONDOWN:
                if ev.button == 1:
                    if self.local_player.tank.ammo:
                        self.send_user_event("shoot", self.local_player, {'target': self.aimer.rect.center})
                        self.local_player.tank.shoot(self.aimer.rect.center)
            elif ev.type == pygame.USEREVENT and ev.details.has_key('id'):
                if ev.details['id'] == self.local_player.player_id and not ev.details['from_host']:
                    self.user_events.append(ev.details)
                else:
                    if self.game.config.host == True:
                        self.user_events.append(ev.details)
                if ev.details['type'] == "rot_left":
                    if ev.details['id'] == self.local_player.player_id:
                        continue
                    self.game.players[ev.details['id']].tank.set_pos(ev.details['pos'])
                    self.game.players[ev.details['id']].tank.set_rot(ev.details['rot'])
                    self.game.players[ev.details['id']].tank.rotate_left()
                elif ev.details['type'] == "rot_right":
                    if ev.details['id'] == self.local_player.player_id:
                        continue
                    self.game.players[ev.details['id']].tank.set_pos(ev.details['pos'])
                    self.game.players[ev.details['id']].tank.set_rot(ev.details['rot'])
                    self.game.players[ev.details['id']].tank.rotate_right()
                elif ev.details['type'] == "forward":
                    if ev.details['id'] == self.local_player.player_id:
                        continue
                    self.game.players[ev.details['id']].tank.set_pos(ev.details['pos'])
                    self.game.players[ev.details['id']].tank.set_rot(ev.details['rot'])
                    self.game.players[ev.details['id']].tank.forward()
                elif ev.details['type'] == "reverse":
                    if ev.details['id'] == self.local_player.player_id:
                        continue
                    self.game.players[ev.details['id']].tank.set_pos(ev.details['pos'])
                    self.game.players[ev.details['id']].tank.set_rot(ev.details['rot'])
                    self.game.players[ev.details['id']].tank.reverse()
                elif ev.details['type'] == "stop":
                    if ev.details['id'] == self.local_player.player_id:
                        continue
                    self.game.players[ev.details['id']].tank.set_pos(ev.details['pos'])
                    self.game.players[ev.details['id']].tank.set_rot(ev.details['rot'])
                    self.game.players[ev.details['id']].tank.stop()
                elif ev.details['type'] == "tur_rot":
                    if ev.details['id'] == self.local_player.player_id:
                        continue
                    self.game.players[ev.details['id']].tank.set_pos(ev.details['pos'])
                    self.game.players[ev.details['id']].tank.set_rot(ev.details['rot'])
                    self.game.players[ev.details['id']].tank.turret.set_rot(ev.details['tRot'])
                elif ev.details['type'] == "shoot":
                    if ev.details['id'] == self.local_player.player_id:
                        continue
                    self.game.players[ev.details['id']].tank.set_pos(ev.details['pos'])
                    self.game.players[ev.details['id']].tank.set_rot(ev.details['rot'])
                    self.game.players[ev.details['id']].tank.shoot(ev.details['misc']['target'])
                elif ev.details['type'] == "round_end":
                    print ev.details
                elif ev.details['type'] == "tank_hit":
                    self.game.players[ev.details['misc']['target']].tank.hit(5, ev.details['id'])
                elif ev.details['type'] == "tank_die":
                    self.game.players[ev.details['misc']['killer']].award_kill()
                    for id in ev.details['misc']['assists']:
                        self.game.players[id].award_assist()
                    self.game.players[ev.details['id']].add_death()
                    self.game.players[ev.details['id']].tank.die()
                    self.score_bar.dirty = True
                elif ev.details['type'] == "grant_ammo":
                    tank = self.game.players[ev.details['id']].tank
                    tank.ammo = ev.details['misc']['pre_grant_ammo']
                    tank.load_ammo()
                elif ev.details['type'] == "obj_heal":
                    tank = self.game.players[ev.details['id']].tank
                    tank.health = ev.details['misc']['pre_heal_health']
                    tank.heal()
        if len(self.user_events):
            if self.game.config.host == False: #if we're not the host, we're just sending our updates to the host
                update = cPickle.dumps(self.user_events)
                update_len = len(update)
                resend = True
                while resend:
                    sent = self.sock.sendto(update, self.game.config.game_host)
                    if not sent < update_len:
                        resend = False
            else:
                update = cPickle.dumps(self.user_events)
                update_len = len(update)
                for p in self.game.players:
                    resend = True
                    while resend:
                        play = self.game.players[p]
                        sent = self.sock.sendto(update, play.addr)
                        if not sent < update_len:
                            resend = False
                        
    def send_user_event(self, type, play, misc=None):
        details = {}
        details['type'] = type
        details['id'] = play.player_id
        details['pos'] = (play.tank.posX, play.tank.posY)
        details['rot'] = play.tank.rot
        details['tRot'] = play.tank.turret.rot
        details['misc'] = misc
        if self.game.config.host:
            details['from_host'] = True
        else:
            details['from_host'] = False
        dict = {}
        dict['details'] = details
        ev = pygame.event.Event(pygame.USEREVENT, dict)
        pygame.event.post(ev)
    
    def stop(self):
        pygame.mouse.set_visible(True)
        self.loop_quit = True
